package com.guanghua.brick.util;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * 用于读取xml文件成Iconfig数据结构的工具类
 * @author leeon
 */
public class XmlReader {
	private final Log logger = LogFactory.getLog(XmlReader.class);

	private Document document = null;

	/** 不允许不带参数实例化 */
	private XmlReader() {}

	
	/**
	 * 通过文件路径来进行加载
	 * @param fileName
	 * @param isFileSystem
	 * @throws Exception
	 */
	public XmlReader(String fileName, boolean isFileSystem) throws Exception {
		try {
			if (isFileSystem) {
				this.loadDocument(new FileInputStream(FileUtil.getFileResource(fileName)));
			} else {
				this.loadDocument(BeanUtil.getClassPathFileByInputStream(fileName));
			}
		} catch (Exception ex) {
			logger.error("Exception in file ["+ fileName +"]", ex);
		}
	}
	
	/**
	 * 通过类路径加载
	 * @param fileName
	 * @throws Exception
	 */
	public XmlReader(String fileName) throws Exception {
		try {
			this.loadDocument(BeanUtil.getClassPathFileByInputStream(fileName));
		} catch (Exception ex) {
			logger.error("Exception in file ["+ fileName +"]", ex);
		}
	}
	
	/**
	 * 通过byte数组加载
	 * @param input
	 * @throws Exception
	 */
	public XmlReader(byte[] input) throws Exception {
		this.loadDocument(new ByteArrayInputStream(input));
	}
	
	/**
	 * 通过输入流加载
	 * @param is
	 * @throws Exception
	 */
	public XmlReader(InputStream is) throws Exception {
		this.loadDocument(is);
	}

	
	/**
	 * 获取一个text节点，包括CDATA的
	 * @param node
	 * @return
	 */
	private String getTextValue(Node node) {
		String textValue = null;
		int length = node.getChildNodes().getLength();

		if (length > 0) {
			textValue = node.getChildNodes().item(0).getNodeValue();

			if (length > 1
					&& (textValue == null || textValue.trim().length() == 0)) {
				textValue = node.getChildNodes().item(1).getNodeValue();
			}

			if (textValue != null) {
				textValue = textValue.trim();
			}
		}
		return textValue;
	}

	
	/**
	 * 将node翻译成IConfig的模式
	 * @param node
	 * @return
	 */
	private IConfig readConfig(Node node) {
		IConfig config = null;

		if (node.getNodeType() == Node.ELEMENT_NODE) {
			//config = (IConfig) BeanFactory.getBean("frm_commonconfig");
			
			config = new CommonConfig();
			config.setName(node.getNodeName());
			config.setText(this.getTextValue(node));

			Attr attribute = null;
			Node childNode = null;
			NamedNodeMap nameNodeMap = ((Element) node).getAttributes();

			for (int i = 0; i < nameNodeMap.getLength(); i++) {
				attribute = (Attr) nameNodeMap.item(i);
				config.setAttribute(attribute.getName(), attribute.getValue());
			}

			for (int i = 0; i < node.getChildNodes().getLength(); i++) {
				childNode = node.getChildNodes().item(i);
				config.setChild(this.readConfig(childNode));
			}
		}

		return config;
	}
	
	/**
	 * 用于装载document的私有方法
	 * @param inputStream
	 * @throws Exception
	 */
	private void loadDocument(InputStream inputStream) throws Exception {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			logger.trace("Using JAXP implementation [" + factory + "]");
			DocumentBuilder docBuilder = factory.newDocumentBuilder();
			this.document = docBuilder.parse(inputStream);
		} catch (ParserConfigurationException e) {
			throw new Exception("Parser configuration exception parsing XML from "+ inputStream, e);
		} catch (SAXParseException e) {
			throw new Exception("Line " + e.getLineNumber()+ " in XML document from " + inputStream + " is invalid", e);
		} catch (SAXException e) {
			throw new Exception("XML document from " + inputStream + " is invalid", e);
		} catch (IOException e) {
			throw new Exception("IOException parsing XML document from " + inputStream, e);
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					logger.warn("Could not close InputStream", e);
				}
			}
		}
	}
	
	
	/**
	 * 读取某个tag name的所有节点
	 * 返回第一个tag name的节点的所有子节点
	 * @param tagName
	 * @return
	 */
	public IConfig[] readChildByTagName(String tagName) {
		IConfig[] configs = null;
		NodeList nodeList = this.document.getElementsByTagName(tagName);

		if (nodeList != null && nodeList.getLength() != 0) {
			IConfig config = null;
			List<IConfig> configList = new ArrayList<IConfig>();

			nodeList = nodeList.item(0).getChildNodes();

			for (int i = 0; i < nodeList.getLength(); i++) {
				config = this.readConfig(nodeList.item(i));

				if (config != null) {
					configList.add(config);
				}
			}
			configs = (IConfig[]) configList.toArray(new IConfig[0]);
		}
		return configs;
	}
	
	
	/**
	 * 读取某个tag name的所有节点
	 * @param tagName
	 * @return
	 */
	public IConfig[] readByTagName(String tagName) {
		NodeList nodeList = this.document.getElementsByTagName(tagName);		
		IConfig config = null;
		List<IConfig> configList = new ArrayList<IConfig>();
		
		for (int i = 0; i < nodeList.getLength(); i++) {
			config = this.readConfig(nodeList.item(i));
			if (config != null) {
				configList.add(config);
			}	
		}
		
		IConfig[] configs = (IConfig[]) configList.toArray(new IConfig[0]);
		return configs;
	}
}
